/*
 * alloc memory method is pmem.
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 * Author: Aeolus <weiqing.yan@ingenic.com>
 */

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/mman.h>

#include <imp/imp_log.h>

#include "alloc_manager.h"
#include "linux_pmem.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "PMEM Method"

#define MEM_DEV "/dev/pmem"
#define CMD_LINE "/proc/cmdline"

static uint32_t pmem_vaddr = 0;
static uint32_t pmem_paddr = 0;
static int pmem_length = 0;

static int get_pmem_size(void)
{
	uint32_t addr = 0;
	int size = 0, ret;
	char buf[512] = "";
	char *p = NULL;

	FILE *fb = fopen(CMD_LINE, "r");
	if(fb == NULL) {
		IMP_LOG_ERR(TAG, "%s open file (%s) error\n", __func__, CMD_LINE);
		return -1;
	}

	ret = fread(buf, 1, sizeof(buf), fb);
	if(ret <= 0) {
		IMP_LOG_ERR(TAG, "%s fread (%s) error\n", __func__, CMD_LINE);
		return -1;
	}

	fclose(fb);

	p = strstr(buf, "pmem");
	if(p == NULL) {
		IMP_LOG_ERR(TAG, "%s fread (%s) error\n", __func__, CMD_LINE);
		return -1;
	}

	sscanf(p, "pmem=%dM@%x", &size, &addr);
	size = size * 1024 * 1024;
	IMP_LOG_DBG(TAG, "CMD Line Pmem Size:%d, Addr:0x%08x\n", size, addr);

	return size;
}

int alloc_pmem_init(Alloc *alloc)
{
	int ret = 0;
	int pmem_fd = -1;
	int pmem_size = 0;

	if(!(alloc)) {
		IMP_LOG_ERR(TAG, "%s function param is NULL\n", __func__);
		return -1;
	}

	pmem_size = get_pmem_size();
	if(pmem_size <= 0) {
		IMP_LOG_ERR(TAG, "get pmem size %d failed\n", pmem_size);
		return -1;
	}

	pmem_fd = open(MEM_DEV, O_RDWR);
	if(pmem_fd <= 0) {
		IMP_LOG_ERR(TAG, "open %s failed\n", MEM_DEV);
		return -1;
	}

	/*sprintf(alloc->mem_alloc.method, "pmem");*/

	alloc->mem_alloc.vaddr = (uint32_t)mmap(0, pmem_size,
			PROT_READ | PROT_WRITE, MAP_SHARED, pmem_fd, 0);
	if(!(alloc->mem_alloc.vaddr)) {
		IMP_LOG_ERR(TAG, "pmem mmap failed\n");
		return -1;
	}

	ret = ioctl(pmem_fd, PMEM_GET_SIZE, &alloc->mem_alloc.length);
	if(ret < 0) {
		IMP_LOG_ERR(TAG, "pmem get size error (ret = %d)\n", ret);
		return ret;
	}

	ret = ioctl(pmem_fd, PMEM_GET_PHYS, &alloc->mem_alloc.paddr);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "pmem get phys address error (ret=%d)\n", ret);
		return ret;
	}

	close(pmem_fd);

	pmem_vaddr = alloc->mem_alloc.vaddr;
	pmem_paddr = alloc->mem_alloc.paddr;
	pmem_length = alloc->mem_alloc.length;

	IMP_LOG_DBG(TAG, "alloc->mem_alloc.method = %s\n \
			alloc->mem_alloc.vaddr = 0x%08x\n \
			alloc->mem_alloc.paddr = 0x%08x\n \
			alloc->mem_alloc.length = %d\n",
			alloc->mem_alloc.method,
			alloc->mem_alloc.vaddr,
			alloc->mem_alloc.paddr,
			alloc->mem_alloc.length);

	return 0;
}

uint32_t alloc_pmem_get_paddr(uint32_t vaddr)
{
	if((vaddr < pmem_vaddr) || (vaddr > (pmem_vaddr + pmem_length))) {
		IMP_LOG_ERR(TAG, "vaddr input error\n");
		return 0;
	}

	return (pmem_paddr + (vaddr - pmem_vaddr));
}

uint32_t alloc_pmem_get_vaddr(uint32_t paddr)
{
	if((paddr < pmem_paddr) || (paddr > (pmem_paddr + pmem_length))) {
		IMP_LOG_ERR(TAG, "paddr input error\n");
		return 0;
	}

	return (pmem_vaddr + (paddr - pmem_paddr));
}
